<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
	<META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=utf-8">
	<TITLE>Отображение отдельно взятого класса (tut1.cpp)</TITLE>
	<META NAME="GENERATOR" CONTENT="LibreOffice 3.5  (Linux)">
	<META NAME="AUTHOR" CONTENT="Viacheslav Naydenov">
	<META NAME="CREATED" CONTENT="20130520;9345500">
	<META NAME="CHANGEDBY" CONTENT="Viacheslav Naydenov">
	<META NAME="CHANGED" CONTENT="20140710;10090100">
	<STYLE TYPE="text/css">
	<!--
		@page { margin: 2cm }
		P { margin-top: 0.1cm; margin-bottom: 0.1cm }
		H2 { margin-bottom: 0.21cm }
		H2.western { font-family: "Times New Roman", serif }
		H2.cjk { font-family: "Droid Sans Fallback" }
		H2.ctl { font-family: "Lohit Hindi" }
		PRE.western { font-family: "Courier New", monospace }
		PRE.cjk { font-family: "Droid Sans Fallback", monospace }
		PRE.ctl { font-family: "Lohit Hindi", monospace }
		TT.western { font-family: "Courier New", monospace }
		TT.cjk { font-family: "Droid Sans Fallback", monospace }
		TT.ctl { font-family: "Lohit Hindi", monospace }
	-->
	</STYLE>
</HEAD>
<BODY LANG="ru-RU" DIR="LTR">
<H2 CLASS="western">Отображение связи один-ко-многим
(tut2.cpp)</H2>
<P>Рассмотрим связь <EM>один-ко-многим</EM>
на примере отношения Клиент — Заказы.
У одного клиента может быть ноль или
более заказов в некоторой учетной
системе, при этом каждый заказ относится
к какому-либо клиенту. Клиенты хранятся
в таблице <TT CLASS="western">client_tbl</TT>, их заказы
— в таблице <TT CLASS="western">order_tbl</TT>.</P>
<P>На уровне SQL эту связь можно выразить
так: на колонке <TT CLASS="western">client_id</TT> в
т.&nbsp;н. <EM>дочерней</EM> таблице <TT CLASS="western">order_tbl</TT>
присутствует <EM>ограничение внешнего
ключа</EM>, ссылающееся на колонку <TT CLASS="western">id</TT>
в т.&nbsp;н. <EM>родительской</EM> таблице
<TT CLASS="western">client_tbl</TT>.</P>
<P>На уровне ORM такая связь представляется
свойствами объектов. Экземпляр класса,
соответствующего дочерней таблице,
обычно имеет <EM>свойство-объектную
ссылку</EM>, которая ссылается на
родительский объект. С&nbsp;другой стороны
такого отношения, экземпляр класса,
соответствующий родительской таблице,
может иметь <EM>свойство-коллекцию</EM>
объектов (которая иногда называется
&quot;backref&quot; - обратная ссылка), чтобы
иметь возможность просмотреть все свои
дочерние объекты.</P>
<P>Вначале определим схему данных,
содержащую два класса <TT CLASS="western">Client</TT>
и <TT CLASS="western">Order</TT>, отображенные в две
таблицы <TT CLASS="western">client_tbl</TT> и <TT CLASS="western">order_tbl</TT>.
Отдельно опишем связь между ними.</P>
<PRE CLASS="western">&lt;schema&gt;
    &lt;table name=&quot;client_tbl&quot; sequence=&quot;client_seq&quot; class=&quot;Client&quot; xml-name=&quot;client&quot;&gt;
        &lt;column name=&quot;id&quot; type=&quot;longint&quot;&gt;
            &lt;primary-key /&gt;
        &lt;/column&gt;
        &lt;column name=&quot;dt&quot; type=&quot;datetime&quot; null=&quot;false&quot; default=&quot;sysdate&quot; /&gt;
        &lt;column name=&quot;name&quot; type=&quot;string&quot; size=&quot;100&quot; null=&quot;false&quot; /&gt;
        &lt;column name=&quot;email&quot; type=&quot;string&quot; size=&quot;100&quot; null=&quot;false&quot; /&gt;
        &lt;column name=&quot;phone&quot; type=&quot;string&quot; size=&quot;50&quot; null=&quot;true&quot; /&gt;
        &lt;column name=&quot;budget&quot; type=&quot;decimal&quot; /&gt;
    &lt;/table&gt;
    &lt;table name=&quot;order_tbl&quot; sequence=&quot;order_seq&quot; class=&quot;Order&quot; xml-name=&quot;order&quot;&gt;
        &lt;column name=&quot;id&quot; type=&quot;longint&quot;&gt;
            &lt;primary-key /&gt;
        &lt;/column&gt;
        &lt;column name=&quot;client_id&quot; type=&quot;longint&quot; null=&quot;false&quot;&gt;
            &lt;foreign-key table=&quot;client_tbl&quot; key=&quot;id&quot;/&gt;
        &lt;/column&gt;
        &lt;column name=&quot;dt&quot; type=&quot;datetime&quot; null=&quot;false&quot; default=&quot;sysdate&quot; /&gt;
        &lt;column name=&quot;memo&quot; type=&quot;string&quot; size=&quot;100&quot; /&gt;
        &lt;column name=&quot;total_sum&quot; type=&quot;decimal&quot; null=&quot;false&quot; /&gt;
        &lt;column name=&quot;paid_sum&quot; type=&quot;decimal&quot; default=&quot;0&quot; /&gt;
        &lt;column name=&quot;paid_dt&quot; type=&quot;datetime&quot; /&gt;
    &lt;/table&gt;
    &lt;relation type=&quot;one-to-many&quot;&gt;
        &lt;one class=&quot;Client&quot; property=&quot;orders&quot; /&gt;
        &lt;many class=&quot;Order&quot; property=&quot;owner&quot; /&gt;
    &lt;/relation&gt;
&lt;/schema&gt;</PRE><P>
В описании таблицы <TT CLASS="western">order_tbl</TT>
указано, что колонка <TT CLASS="western">client_id</TT>,
является внешним ключом, она играет
роль ссылки. Когда между двумя таблицами
существует ровно одна подобная связь,
то при описании связи с помощью элемента
<TT CLASS="western">&lt;relation&gt;</TT> нет необходимости
в дополнительных указаниях.</P>
<P>Код SQLite для приведенной выше схемы
(использование утилиты <TT CLASS="western">yborm_gen</TT>
см. в <A HREF="Tutorial1.ru.html">Tutorial1</A>) будет
выглядеть так:</P>
<PRE CLASS="western">CREATE TABLE client_tbl (
    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
    name VARCHAR(100) NOT NULL,
    email VARCHAR(100) NOT NULL,
    phone VARCHAR(50),
    budget NUMERIC
);

CREATE TABLE order_tbl (
    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    client_id INTEGER NOT NULL,
    dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
    memo VARCHAR(100),
    total_sum NUMERIC NOT NULL,
    paid_sum NUMERIC DEFAULT 0,
    paid_dt TIMESTAMP
    , FOREIGN KEY (client_id) REFERENCES client_tbl(id)
);</PRE><P>
Следующая программа использует доменные
классы из приведенной выше схемы
(использование утилиты <TT CLASS="western">yborm_gen</TT>
см. в <A HREF="Tutorial1.ru.html">Tutorial1</A>). Эта программа
сначала создаёт дочерний объект,
присваивает какие-то значения его полям,
затем она создаёт родительский объект,
потом она связывает дочерний объект с
родительским. В конце она сохраняет
объекты в сессии и записывает их в базу,
завершая транзакцию (<TT CLASS="western">session.commit();</TT>).</P>
<PRE CLASS="western">#include &lt;iostream&gt;
#include &quot;domain/Client.h&quot;
#include &quot;domain/Order.h&quot;
int main()
{
    std::auto_ptr&lt;Yb::SqlConnection&gt; conn(new Yb::SqlConnection(
            &quot;sqlite+sqlite://./tut2.sqlite&quot;));
    Yb::Engine engine(Yb::Engine::READ_WRITE, conn);
    Yb::Session session(Yb::init_schema(), &amp;engine);

    Domain::Order::Holder order;
    std::string amount;
    std::cout &lt;&lt; &quot;Enter order amount: \n&quot;;
    std::cin &gt;&gt; amount;
    order-&gt;total_sum = Yb::Decimal(amount);

    Domain::Client::Holder client;
    std::string name, email;
    std::cout &lt;&lt; &quot;Enter name, email: \n&quot;;
    std::cin &gt;&gt; name &gt;&gt; email;
    client-&gt;name = name;
    client-&gt;email = email;
    client-&gt;dt = Yb::now();

    std::cout &lt;&lt; &quot;Client's orders count: &quot; &lt;&lt; client-&gt;orders.size() &lt;&lt; &quot;\n&quot;;
    order-&gt;owner = client;
    std::cout &lt;&lt; &quot;Client's orders count: &quot; &lt;&lt; client-&gt;orders.size() &lt;&lt; &quot;\n&quot;;

    order-&gt;save(session);
    client-&gt;save(session);
    session.commit();
    std::cout &lt;&lt; order-&gt;xmlize(1)-&gt;serialize() &lt;&lt; std::endl;
    return 0;
}</PRE><P>
В этом примере видно, что связывание
дочернего (класс <TT CLASS="western">Order</TT>)
объекта с родительским (класс <TT CLASS="western">Client</TT>)
выглядит как присваивание (<TT CLASS="western">order-&gt;owner
= client;</TT>). Однако, этот пример оперирует
с объектами доменных классов не напрямую,
как в <A HREF="Tutorial1.ru.html">Tutorial1</A>, вместо
этого используются классы
<TT CLASS="western">&lt;DomainClass&gt;::Holder</TT>, которые
необходимы для реализации вложенных и
рекурсивных объектных ссылок. То есть,
все свойства-объектные ссылки реализованы
с использованием классов <TT CLASS="western">&lt;DomainClass&gt;::Holder</TT>,
а для их разыменования нужно использовать
стрелочку (<TT CLASS="western">-&gt;</TT>), а не точку
(<TT CLASS="western">.</TT>). 
</P>
<P>Компилируем пример:</P>
<PRE CLASS="western">$ c++ -I. -I$YBORM_ROOT/include/yb -o tut2 tut2.cpp domain/Client.cpp domain/Order.cpp -L$YBORM_ROOT/lib -lybutil -lyborm</PRE><P>
Запускаем на выполнение:</P>
<PRE CLASS="western">$ ./tut2
Enter order amount: 
45.67
Enter name, email: 
Petya pet@ya.ru
Client's orders count: 0
Client's orders count: 1
&lt;order&gt;&lt;id&gt;2&lt;/id&gt;&lt;owner&gt;&lt;id&gt;2&lt;/id&gt;&lt;dt&gt;2014-06-01T17:25:32&lt;/dt&gt;&lt;name&gt;Petya&lt;/name&gt;&lt;email&gt;pet@ya.ru&lt;/email&gt;&lt;budget is_null=&quot;1&quot;/&gt;&lt;/owner&gt;&lt;dt&gt;2014-06-01T17:25:17&lt;/dt&gt;&lt;memo is_null=&quot;1&quot;/&gt;&lt;total-sum&gt;45.67&lt;/total-sum&gt;&lt;paid-sum&gt;0&lt;/paid-sum&gt;&lt;paid-dt is_null=&quot;1&quot;/&gt;&lt;/order&gt;</PRE><P>
Если проследить операторы SQL, включив
журнал сообщений по примеру <A HREF="Tutorial1.ru.html">Tutorial1</A>,
то увидим следующие подробности:</P>
<PRE CLASS="western">14-06-01 17:25:32.293 31927/31927 DEBG orm: flush started
14-06-01 17:25:32.293 31927/31927 DEBG sql: begin transaction
14-06-01 17:25:32.293 31927/31927 DEBG sql: prepare: INSERT INTO client_tbl (dt, name, email, budget) VALUES (?, ?, ?, ?)
14-06-01 17:25:32.294 31927/31927 DEBG sql: bind: (DateTime, String, String, Decimal)
14-06-01 17:25:32.294 31927/31927 DEBG sql: exec prepared: p1=&quot;'2014-06-01 17:25:32'&quot; p2=&quot;'Petya'&quot; p3=&quot;'pet@ya.ru'&quot; p4=&quot;NULL&quot;
14-06-01 17:25:32.295 31927/31927 DEBG sql: prepare: SELECT SEQ LID FROM SQLITE_SEQUENCE WHERE NAME = 'client_tbl'
14-06-01 17:25:32.295 31927/31927 DEBG sql: exec prepared:
14-06-01 17:25:32.295 31927/31927 DEBG sql: fetch: LID='2' 
14-06-01 17:25:32.295 31927/31927 DEBG sql: fetch: no more rows
14-06-01 17:25:32.295 31927/31927 DEBG sql: prepare: INSERT INTO order_tbl (client_id, dt, memo, total_sum, paid_sum, paid_dt) VALUES (?, ?, ?, ?, ?, ?)
14-06-01 17:25:32.295 31927/31927 DEBG sql: bind: (LongInt, DateTime, String, Decimal, Decimal, DateTime)
14-06-01 17:25:32.295 31927/31927 DEBG sql: exec prepared: p1=&quot;2&quot; p2=&quot;'2014-06-01 17:25:17'&quot; p3=&quot;NULL&quot; p4=&quot;45.67&quot; p5=&quot;0&quot; p6=&quot;NULL&quot;
14-06-01 17:25:32.295 31927/31927 DEBG sql: prepare: SELECT SEQ LID FROM SQLITE_SEQUENCE WHERE NAME = 'order_tbl'
14-06-01 17:25:32.296 31927/31927 DEBG sql: exec prepared:
14-06-01 17:25:32.296 31927/31927 DEBG sql: fetch: LID='2' 
14-06-01 17:25:32.296 31927/31927 DEBG sql: fetch: no more rows
14-06-01 17:25:32.296 31927/31927 DEBG orm: flush finished OK
14-06-01 17:25:32.296 31927/31927 DEBG sql: commit
14-06-01 17:25:32.395 31927/31927 DEBG sql: prepare: SELECT order_tbl.id, order_tbl.client_id, order_tbl.dt, order_tbl.memo, order_tbl.total_sum, order_tbl.paid_sum, order_tbl.paid_dt FROM order_tbl WHERE order_tbl.id = ?
14-06-01 17:25:32.395 31927/31927 DEBG sql: exec prepared: p1=&quot;2&quot;
14-06-01 17:25:32.395 31927/31927 DEBG sql: fetch: ID='2' CLIENT_ID='2' DT='2014-06-01T17:25:17' MEMO=NULL TOTAL_SUM='45.67' RECEIPT_SUM='0' RECEIPT_DT=NULL 
14-06-01 17:25:32.395 31927/31927 DEBG sql: fetch: no more rows
14-06-01 17:25:32.395 31927/31927 DEBG sql: prepare: SELECT client_tbl.id, client_tbl.dt, client_tbl.name, client_tbl.email, client_tbl.budget FROM client_tbl WHERE client_tbl.id = ?
14-06-01 17:25:32.396 31927/31927 DEBG sql: exec prepared: p1=&quot;2&quot;
14-06-01 17:25:32.396 31927/31927 DEBG sql: fetch: ID='2' DT='2014-06-01T17:25:32' NAME='Petya' EMAIL='pet@ya.ru' BUDGET=NULL 
14-06-01 17:25:32.396 31927/31927 DEBG sql: fetch: no more rows</PRE><P>
Обратите внимание, что объекты вставлены
в корректной последовательности (первым
— родительский, вторым — дочерний), так
как используется топологическая
сортировка графа зависимых объектов.
Значение внешнего ключа будет присвоено
автоматически, так же как и значения
первичных ключей.</P>
<P>При связывании объектов с помощью
присваивания происходит и изменение
коллекции <TT CLASS="western">orders</TT> в соответствующем
экземпляре класса <TT CLASS="western">Client</TT>.
Дело в том, что оба свойства <TT CLASS="western">client-&gt;orders</TT>
и <TT CLASS="western">order-&gt;owner</TT> используют один
и тот же внутренний объект <TT CLASS="western">RelationObject</TT>.
Можно добиться такого же эффекта вставкой
в свойство-коллекцию. Заменяем присваивание
со стороны подчиненного объекта</P>
<PRE CLASS="western">    order-&gt;owner = client;</PRE><P>
на вставку в коллекцию со стороны
главного объекта</P>
<PRE CLASS="western">    client-&gt;orders.insert(*order);</PRE><P>
Можно увидеть, что оба эти варианта
абсолютно равносильны.</P>
</BODY>
</HTML>
